package com.pbd.server;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.proxy.HibernateProxy;


public class Convertor {

	private static Map objectMAPs = new IdentityHashMap();

	private static Class getAssociatedObjectClass(Map mainClasssNames, String columnExpression, String aliasName) {
		Class clazz = (Class) mainClasssNames.get(aliasName);
		if (clazz != null) {
			Method[] clazzMethods = clazz.getDeclaredMethods();
			int clazzMethodsLength = clazzMethods == null ? 0 : clazzMethods.length;
			for (int i = 0; i < clazzMethodsLength; i++) {
				Method method = clazzMethods[i];
				String methodName = method.getName();
				if (!methodName.startsWith("get"))
					continue;
				methodName = methodName.substring(3);
				if (methodName.equalsIgnoreCase(columnExpression)) {
					return method.getReturnType();
				}
			}
		}
		return null;
	}

	public static Object convert(Object object) {
		if (object == null) {
			return null;
		}
		Class objectClass = object.getClass();
		objectClass = getClass(objectClass);
		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~Class =" + objectClass.getName());
		Object returnObject = null;
		try {
			returnObject = objectClass.newInstance();
			objectMAPs.put(object, returnObject);
			Method[] methods = objectClass.getDeclaredMethods();
			int length = methods == null ? 0 : methods.length;
			for (int i = 0; i < length; i++) {
				if (!methods[i].getName().startsWith("get"))
					continue;
				Class methodReturnType = methods[i].getReturnType();
				String methodName = methods[i].getName();

				HibernateServiceImpl hibernateServiceImpl = new HibernateServiceImpl();
				String columnName = hibernateServiceImpl.getPrimaryColumnName(objectClass.getName());

				if (columnName.equalsIgnoreCase(methodName.substring(3))) {
					continue;
				}
				methodName = "set" + methodName.substring(3);
				try {
					if (isPrimitive(methodReturnType)) {
						Class[] paramType = new Class[1];
						paramType[0] = methodReturnType;
						Object param[] = new Object[1];
						param[0] = methods[i].invoke(object, null);
						Method method = returnObject.getClass().getMethod(methodName, paramType);
						method.invoke(returnObject, param);
					} else if (methodReturnType == byte[].class) {
						Class[] paramType = new Class[1];
						paramType[0] = methodReturnType;
						Object param[] = new Object[1];
						param[0] = methods[i].invoke(object, null);
						Method method = returnObject.getClass().getMethod(methodName, paramType);
						method.invoke(returnObject, param);
					} else if (isDate(methodReturnType)) {
						Class[] paramType = new Class[1];
						paramType[0] = methodReturnType;
						Method method = returnObject.getClass().getMethod(methodName, paramType);
						Object param[] = new Object[1];
						param[0] = convertToDate((Date) methods[i].invoke(object, null));
						method.invoke(returnObject, param);
					} else if (implementsCollection(methodReturnType)) {
						Class[] paramType = new Class[1];
						paramType[0] = methodReturnType;
						Collection sourceCollection = (Collection) methods[i].invoke(object, null);
						Object param[] = new Object[1];
						String cascadeStr = hibernateServiceImpl.getCascadeValue(objectClass.getName(), methodName.substring(3));
						if (cascadeStr != null && cascadeStr.trim().equalsIgnoreCase("SAVE_UPDATE"))
							param[0] = convertToList(sourceCollection);
						else
							param[0] = sourceCollection;
						Method method = returnObject.getClass().getMethod(methodName, paramType);
						method.invoke(returnObject, param);
					} else if (!isPrimitive(methodReturnType) && !(methodReturnType == HibernateProxy.class) && !(methodReturnType == Class.class) && !(methodReturnType == Iterator.class)) {
						Object dataObect = (Object) methods[i].invoke(object, null);
						Class[] paramType = new Class[1];
						paramType[0] = methodReturnType;
						Method method = returnObject.getClass().getMethod(methodName, paramType);
						Object param[] = new Object[1];

						if (objectMAPs.containsKey(dataObect))
							param[0] = objectMAPs.get(dataObect);
						else {
							String cascadeStr = hibernateServiceImpl.getCascadeValue(objectClass.getName(), methodName.substring(3));
							if (cascadeStr != null && cascadeStr.trim().equalsIgnoreCase("SAVE_UPDATE"))
								param[0] = convert(dataObect);
							else
								param[0] = dataObect;
						}
						method.invoke(returnObject, param);
					}
				} catch (NoSuchMethodException e) {
					System.out.println("Transient method call for method name : [" + methodName + "]");
					e.printStackTrace();
					continue;
				}
			}
			return returnObject;
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("Convertor objects " + e);
		}
	}

	private static boolean isDate(Class objectClass) {
		if (objectClass == Date.class)
			return true;
		Class clas = objectClass;
		boolean isDate = false;
		do {
			if (clas == Date.class) {
				isDate = true;
				break;
			}
			clas = clas.getSuperclass();
		} while (clas != null);
		return isDate;
	}

	private static Date convertToDate(Date date) {
		if (date == null)
			return null;
		return new Date(date.getTime());
	}

	private static boolean implementsCollection(Class objectClass) {
		if (objectClass == Collection.class)
			return true;
		ArrayList interfaces = new ArrayList();
		Class superClass = objectClass;
		do {
			interfaces.add(Arrays.asList(superClass.getInterfaces()));
			superClass = superClass.getSuperclass();
		} while (superClass != null);
		boolean isCollection = false;
		for (int j = 0; j < interfaces.size(); j++)
			if (interfaces.get(j) == Collection.class) {
				isCollection = true;
				break;
			}
		return isCollection;
	}

	private static List convertToList(Collection collection) {
		List targetList = new ArrayList();
		Object[] objArray = collection == null ? null : collection.toArray();
		int length = objArray == null ? 0 : objArray.length;
		for (int i = 0; i < length; i++) {
			Object obj = objArray[i];
			if (obj == null)
				continue;
			if (objectMAPs.containsKey(obj))
				targetList.add(objectMAPs.get(obj));
			else
				targetList.add(convert(obj));
		}
		return targetList;
	}

	private static boolean isPrimitive(Class objectClass) {
		if (objectClass.isPrimitive() || objectClass == Boolean.class || objectClass == Character.class || objectClass == Byte.class || objectClass == Short.class || objectClass == Integer.class || objectClass == Long.class || objectClass == Float.class || objectClass == Double.class || objectClass == String.class || objectClass == Void.class)
			return true;
		else
			return false;
	}

	public static void clearData() {
		objectMAPs.clear();
	}

	private static Class getClass(Class actualClazz) {
		if (actualClazz != null) {
			String clazzName = actualClazz.getName();
			String ch = "$";
			if (clazzName.indexOf("_$") != -1) {
				ch = "_$";
				clazzName = clazzName.substring(0, clazzName.indexOf(ch));
			} else {
				if (clazzName.indexOf("$") != -1) {
					clazzName = clazzName.substring(0, clazzName.indexOf("$"));
				}
			}
			try {
				actualClazz = Class.forName(clazzName);
			} catch (ClassNotFoundException e) {
				throw new IllegalArgumentException("Passed className is invalid. You have passed wrong className [" + clazzName);
			}
		}
		return actualClazz;
	}
}
